﻿#region Using Directives

    using System;
    using System.Collections.Generic;
    using System.Data.Linq;
    using System.Linq;
    using System.Text;

    using Csla;
    using Csla.Core;
    using Csla.Data;
    using Csla.Reflection;
    using Csla.Security;
    using Csla.Server;

    using MarketPulse.BusinessLogic.Core.Security;
    using MarketPulse.BusinessLogic.Core.System;
    using MarketPulse.BusinessLogic.Core;
    using MarketPulse.BusinessLogic.Core.Criteria;

#endregion

namespace MarketPulse.DataAccess.Factory
{
    /// <summary>
    /// Abstracts the data access for the Security away from the Security Business Objects.
    /// </summary>
    public class SecurityFactory : ObjectFactory
    {
        #region Member Variables

            /// <summary>
            /// Property that stores the name of the class.
            /// </summary>
            private static readonly Type mClass = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType;

            /// <summary>
            /// Property that stores the log4net logger for this class.
            /// </summary>
            private static readonly log4net.ILog mLog = log4net.LogManager.GetLogger(mClass);

        #endregion

        #region Fetch Methods

            /// <summary>
            /// Method used to construct a security identity by attempting to authenticate a user based on the criteria supplied
            /// </summary>
            /// <param name="criteria">The criteria object to use</param>
            /// <returns>A valid (filled) identity object</returns>
            public MarketPulseIdentity Fetch(SystemAuthenticationCriteria criteria)
            {
                try
                {
                    using (var manager = ContextManager<DataContextObjects.Production.MUM_dev>.GetManager(Database.BusinessSystem))
                    {
                        var identity = (MarketPulseIdentity)MethodCaller.CreateInstance(typeof(MarketPulseIdentity));

                        // Set up some local variables
                        bool authenticated = false;
                        ApplicationProfile loginApplicationProfile = null;
                        DateTime dateTime = DateTime.UtcNow;

                        // Set up some standard audit object values
                        AuditUserLogin auditUserLogin = AuditUserLogin.New();
                        auditUserLogin.ProfileTypeGUID = criteria.ProfileTypeGUID;
                        auditUserLogin.AuditUserLoginResultGUID = Guid.Empty;
                        auditUserLogin.InsertDateTime = dateTime.ToString();

                        // load the login users details
                        if (criteria.UserGUID.HasValue)
                        {
                            loginApplicationProfile = ApplicationProfile.Get(criteria.UserGUID.Value, criteria.ProfileTypeGUID);
                        }
                        else
                        {
                            loginApplicationProfile = ApplicationProfile.Get(criteria.Username, criteria.ProfileTypeGUID);
                        }

                        // Set the audit object fields
                        auditUserLogin.UserGUID = loginApplicationProfile.UserGUID.Equals(Guid.Empty) ? criteria.UserGUID : loginApplicationProfile.UserGUID;
                        auditUserLogin.Username = loginApplicationProfile.Username.Equals(String.Empty) ? criteria.Username : loginApplicationProfile.Username;

                        if (loginApplicationProfile.UserGUID != Guid.Empty)
                        {
                            // Is the user enabled
                            if (loginApplicationProfile.Enabled)
                            {
                                // Is the user not obsolete
                                if (!loginApplicationProfile.Obsolete)
                                {
                                    // Do the passwords match
                                    if (!String.IsNullOrEmpty(criteria.Password) && loginApplicationProfile.Password == criteria.Password)
                                    {
                                        authenticated = true;
                                    }
                                    else if (!String.IsNullOrEmpty(criteria.PrivateKey) && loginApplicationProfile.PrivateKey == criteria.PrivateKey)
                                    {
                                        // Passwords did not match, check if privateKey matches
                                        authenticated = true;
                                    }
                                    else
                                    {
                                        // Neither password nor private key matched
                                        auditUserLogin.AuditUserLoginResultGUID = Guid.Parse("3D2B7D38-67A2-4FC6-8E4B-631967A5E5A1");
                                    }
                                }
                                else
                                {
                                    // Account is Obsolete
                                    auditUserLogin.AuditUserLoginResultGUID = Guid.Parse("DACF9879-4EC1-4670-9993-73F52BC0CCC3");
                                }
                            }
                            else
                            {
                                // Account is disabled
                                auditUserLogin.AuditUserLoginResultGUID = Guid.Parse("BF6CBF95-6D8D-4BFE-91FB-E3CF8BACD383");
                            }

                            // Set up the identity to return
                            if (authenticated)
                            {
                                LoadProperty(identity, MarketPulseIdentity.NameProperty, loginApplicationProfile.Username);
                                LoadProperty(identity, MarketPulseIdentity.IsAuthenticatedProperty, true);

                                var rolesSPResult = manager.DataContext.UspUserUserRoleSelectByUserGUID(loginApplicationProfile.UserGUID);

                                MobileList<string> roleList = new MobileList<string>();
                                foreach (DataContextObjects.Production.UspUserUserRoleSelectByUserGUIDResult rolesResult in rolesSPResult)
                                {
                                    roleList.Add(rolesResult.UserRoleName);
                                }

                                LoadProperty(identity, MarketPulseIdentity.RolesProperty, roleList);

                                //TODO refactor code to maybe move user updating somewhere else!!
                                // Update the user account

                                loginApplicationProfile.FailedLoginAttempts = 0;
                                loginApplicationProfile.SuccessfulLogonCount += 1;
                                loginApplicationProfile.LastLogonDateTime = dateTime;

                            }
                            else
                            {
                                //TODO refactor code to maybe move user updating somewhere else!!
                                // Update the user account

                                // Increment the failed logon attempts and decide whether to disable the account
                                if (loginApplicationProfile.FailedLoginAttempts < 5)
                                {
                                    loginApplicationProfile.FailedLoginAttempts += 1;
                                }
                                else
                                {
                                    if (loginApplicationProfile != null)
                                    {
                                        loginApplicationProfile.Enabled = false;
                                    }
                                }

                                // Setup the Unauthenticated Identity
                                LoadProperty(identity, MarketPulseIdentity.NameProperty, string.Empty);
                                LoadProperty(identity, MarketPulseIdentity.IsAuthenticatedProperty, false);
                                LoadProperty(identity, MarketPulseIdentity.RolesProperty, null);
                            }
                        }
                        else
                        {
                            // No user or profile found with that username
                            auditUserLogin.AuditUserLoginResultGUID = Guid.Parse("AFF9365E-430A-4160-B5E7-D39FD00F8EAD");
                        }

                        // Save the changes to the user login account
                        // If the profile has failed to load then it will have no GUID value
                        if (loginApplicationProfile.GUID.HasValue)
                        {
                            loginApplicationProfile.Save();
                        }

                        // If the auditUserLogin has benn assigned a resultGUID then a fialed logtin has occurred
                        if (auditUserLogin.AuditUserLoginResultGUID != Guid.Empty)
                        {
                            auditUserLogin.Save();
                        }

                        return identity;
                    }
                }
                catch (InvalidOperationException ioe)
                {
                    // Construct error message
                    StringBuilder errorMessage = new StringBuilder();
                    errorMessage.Append("Unable to locate profile with Guid = [ ");
                    errorMessage.Append(criteria.UserGUID);
                    errorMessage.Append(" ]");
                    errorMessage.Append(" and Username = [ ");
                    errorMessage.Append(criteria.Username);
                    errorMessage.Append(" ]");
                    mLog.Error(errorMessage.ToString(), ioe);
                    throw ioe;
                }
                catch (Exception ex)
                {
                    mLog.Error(ex.ToString());
                    throw ex;
                }
            }

        #endregion

    }
}
